import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:ylm_ui_kit/ylm_ui_kit.dart';

typedef ClickListener = void Function(int i);
typedef CalculationDot = double Function(double page);

class SeBanner extends StatelessWidget {
  const SeBanner({
    Key? key,
    required this.itemBuilder,
    required this.controller,
    required this.itemHeight,
    this.commonBannerIndicator,
    this.onPageChanged,
    this.bg = Colors.white54,
    this.scrollDirection = Axis.horizontal,
  }) : super(key: key);

  ///控制器
  final CommonBannerController controller;

  ///子UI
  final IndexedWidgetBuilder itemBuilder;

  ///控件的高度
  final double itemHeight;

  ///指示器
  final CommonBannerIndicator? commonBannerIndicator;

  ///界面切换
  final ValueChanged<int>? onPageChanged;

  ///背景
  final Color bg;

  ///轮播方向
  final Axis scrollDirection;

  ///banner
  Widget banner(BuildContext context) {
    List<Widget> children = <Widget>[];
    for (int i = 0; i < controller.itemCount; i++) {
      children.add(itemBuilder(context, i));
    }
    var realIndex = (index) => index;
    int jumpTo = -1;
    if (controller._recycle) {
      //头尾相接
      children = [
        children[controller.itemCount - 1],
        ...children,
        children[0],
      ];
      realIndex = (index) {
        //处理接触点
        jumpTo = -1;
        if (index == 0) {
          index = controller.itemCount - 1;
          jumpTo = controller.itemCount;
        } else if (index == controller.itemCount + 1) {
          index = 0;
          jumpTo = 1;
        } else {
          index--;
        }
        return index;
      };
      SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
        controller._controller.jumpToPage(controller._controller.initialPage);
      });
    }
    controller._controller = PageController(initialPage: controller._curPage);
    commonBannerIndicator?.initPage(controller._curPage == 0 ? 0.0 : realIndex(controller._curPage) * 1.0);
    return NotificationListener<ScrollNotification>(
      onNotification: (ScrollNotification notification) {
        if (notification is ScrollEndNotification && jumpTo != -1) {
          int temp = jumpTo;
          jumpTo = -1;
          controller._controller.jumpToPage(temp);
        }
        if (controller._recycle) {
          if (controller._controller.page! >= 1 && controller._controller.page! <= controller.itemCount) {
            commonBannerIndicator?.onPageChange(controller._controller.page! - 1);
          }
        } else {
          commonBannerIndicator?.onPageChange(controller._controller.page!);
        }
        return false;
      },
      child: PageView(
        onPageChanged: (index) {
          controller._curPage = index;
          int curPage = realIndex(index);
          if (jumpTo != -1) {
            return;
          }
          commonBannerIndicator?.onPageSelect(curPage);
          onPageChanged?.call(curPage);
        },
        controller: controller._controller,
        scrollDirection: scrollDirection,
        children: children,
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    if (controller.itemCount == 0) {
      return Container(height: itemHeight);
    }
    //banner
    var children = <Widget>[banner(context)];
    //指示器
    if (commonBannerIndicator != null) {
      children.add(commonBannerIndicator!.initIndicator(controller.itemCount, itemHeight));
    }
    //容器
    return Listener(
      onPointerDown: (event) => controller.stopPlay(),
      onPointerCancel: (event) => controller.startPlay(),
      onPointerUp: (event) {
        if (controller.autoStart) {
          controller.startPlay();
        }
      },
      child: Container(
        color: bg,
        height: itemHeight,
        child: Stack(children: children),
      ),
    );
  }
}

class CommonBannerController {
  CommonBannerController(
    this.itemCount, {
    this.delayTime = 2000,
    this.scrollTime = 300,
    bool recycle = true,
    this.autoStart = true,
  }) : _recycle = recycle && (itemCount > 1) {
    if (autoStart) startPlay();
  }

  Timer? _timer;

  ///自动轮播的间隔时间
  final int delayTime;

  ///自动轮播的切换时间
  final int scrollTime;

  ///数量
  final int itemCount;

  ///是否可以循环
  final bool _recycle;

  ///自动播放
  final bool autoStart;

  PageController _controller = PageController(initialPage: 1);
  int _curPage = 1;

  ///开始轮播
  void startPlay() {
    stopPlay();
    if (_timer != null) {
      _timer!.cancel();
    }
    _timer = Timer.periodic(Duration(milliseconds: delayTime), (timer) {
      if (_controller.positions.isEmpty) return;
      _controller.nextPage(
        duration: Duration(milliseconds: scrollTime),
        curve: Curves.linear,
      );
    });
  }

  ///停止轮播
  void stopPlay() {
    if (_timer == null) return;
    _timer?.cancel();
    _timer = null;
  }

  void release() {
    stopPlay();
  }
}

///指示器
abstract class CommonBannerIndicator {
  ValueNotifier<double>? page;

  ///页面切换完成
  void onPageSelect(int position) {}

  ///页面切换中
  void onPageChange(double page) {}

  ///初始化指示器
  Widget initIndicator(int itemCount, double bannerHeight);

  ///初始化位置
  void initPage(double curPage) {
    page = ValueNotifier(curPage);
  }
}

class CircleAndOvalIndicator extends CommonBannerIndicator {
  CircleAndOvalIndicator(
    this.dotColor,
    this.chooseDotColor,
  );

  final Color dotColor;
  final Color chooseDotColor;

  @override
  Widget initIndicator(int itemCount, double bannerHeight) {
    return Align(
      alignment: Alignment(0, 1 - 10.w / bannerHeight),
      child: Stack(
        clipBehavior: Clip.none,
        children: [
          Row(mainAxisSize: MainAxisSize.min, children: [
            for (int i = 0; i < itemCount; i++) dot(i),
          ]),
          ValueListenableBuilder<double>(
            valueListenable: page!,
            builder: (context, value, child) {
              double left = value * 10.w - 1.w;
              return Positioned(
                left: left,
                child: chooseDot(),
              );
            },
          )
        ],
      ),
    );
  }

  Widget dot(int i) {
    return Container(
      width: 5.w,
      height: 5.w,
      margin: i < 0 ? null : EdgeInsets.only(left: i == 0 ? 0 : 5.w),
      decoration: CommonDecoration.getShapeDecoration(bg: dotColor),
    );
  }

  Widget chooseDot() {
    return Container(
      width: 7.w,
      height: 5.w,
      decoration: CommonDecoration.getShapeDecoration(bg: chooseDotColor),
    );
  }

  @override
  void onPageChange(double page) {
    this.page!.value = page;
  }
}

class CircleAndCircle extends CommonBannerIndicator {
  CircleAndCircle(
    this.dotColor,
    this.chooseDotColor, {
    this.dotMargin = 19,
    this.dotSize = 9,
    this.alignment,
  });

  final Color dotColor;
  final Color chooseDotColor;
  final double dotMargin;
  final double dotSize;
  Alignment? alignment;

  @override
  Widget initIndicator(int itemCount, double bannerHeight) {
    return Align(
      alignment: alignment ?? const Alignment(0, 1),
      child: Stack(
        clipBehavior: Clip.none,
        children: [
          Row(mainAxisSize: MainAxisSize.min, children: [
            for (int i = 0; i < itemCount; i++) dot(i),
          ]),
          ValueListenableBuilder<double>(
            valueListenable: page!,
            builder: (context, value, child) {
              double left = value * (dotMargin + dotSize);
              return Positioned(
                left: left,
                child: chooseDot(),
              );
            },
          )
        ],
      ),
    );
  }

  Widget dot(int i) {
    return Container(
      width: dotSize,
      height: dotSize,
      margin: i < 0 ? null : EdgeInsets.only(left: i == 0 ? 0 : dotMargin),
      decoration: CommonDecoration.getShapeDecoration(bg: dotColor),
    );
  }

  Widget chooseDot() {
    return Container(
      width: dotSize,
      height: dotSize,
      decoration: CommonDecoration.getShapeDecoration(bg: chooseDotColor),
    );
  }

  @override
  void onPageChange(double page) {
    this.page!.value = page;
  }
}
